package com.eascs.web.sso;

import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.alibaba.dubbo.config.annotation.Reference;
import com.eascs.app.context.ConfigContext;
import com.eascs.app.log.Logger;
import com.eascs.app.log.LoggerFactory;
import com.eascs.app.runtime.RuntimeSetting;
import com.eascs.common.util.ApplicationContextUtil;
import com.eascs.common.util.StringUtil;
import com.eascs.common.util.UrlUtil;
import com.eascs.site.controller.BaseViewController;
import com.eascs.site.cookie.CookieSpec;
import com.eascs.site.session.SessionSpec;
import com.eascs.web.context.RequestContext;
import com.eascs.web.context.WebSetting;
import com.eascs.web.exception.RedirectException;

public class SsoUserInterceptor extends HandlerInterceptorAdapter {
	public static final String FSAPP = "eascsfsapp";
	private final Logger logger = LoggerFactory.getLogger(SsoUserInterceptor.class);
	public static final SessionSpec<SsoUser> SESSIONSPEC_SSOUSER = new SessionSpec<>(SsoUser.class.getName());
	public static final String PARAM_VALIDID = "_vid";
	public static CookieSpec COOKIESPEC_VALIDID_ALL_DOMAIN = null;
	public static final long LAST_UPDATE_INTERVAL = 1000L * 60 * 10;

	@Reference
	private SsoUserService ssoUserService;

	@Reference(version = "ssoUserServiceApp")
	private SsoUserServiceWithVersion ssoUserServiceApp;

	public static CookieSpec buildVailIdCookieSpec() {
		String domain = WebSetting.get().getServerName();
		if (domain.endsWith(".eascs.com")) {
			domain = ".eascs.com";
		} else if (domain.endsWith(".51eascs.com")) {
			domain = ".51eascs.com";
		}

		String env = RuntimeSetting.get().getEnv();
		if (env.indexOf("{") >= 0) {
			env = "dev";
		}
		String name = "vid_" + env;
		return CookieSpec.build(name).httpOnly(true).path("/").domain(domain);

	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		if (isIgnoreRequestUrl()) {
			return super.preHandle(request, response, handler);
		}

		iniCookieSpec();
		String validId = COOKIESPEC_VALIDID_ALL_DOMAIN.getValue();
		if (StringUtil.hasText(RequestContext.getParameter(SsoUserInterceptor.PARAM_VALIDID))) {
			if ("127.0.0.1".equals(WebSetting.get().getServerName()) || "localhost".equals(WebSetting.get().getServerName())) {
				validId = RequestContext.getParameter(SsoUserInterceptor.PARAM_VALIDID);
				COOKIESPEC_VALIDID_ALL_DOMAIN.setValue(validId);
				logger.debug("update validId from param");
			}
		}
		if (StringUtil.isEmpty(validId)) {
			// login
			toGetValidId(handler);
		}
		SsoUser ssoUser = SESSIONSPEC_SSOUSER.getValue();
		long now = new Date().getTime();
		if (null == ssoUser || !validId.equals(ssoUser.getValidId()) || now - ssoUser.getLastUpdateDate() > LAST_UPDATE_INTERVAL) {
			logger.debug("build new sso user");
			ssoUser = new SsoUser();
			ssoUser.setUser(getUserWithValidId(validId));
			ssoUser.setValidId(validId);
			ssoUser.setLastUpdateDate(now);
			if (null == ssoUser.getUser() || StringUtil.isEmpty(ssoUser.getUser().getNo())) {
				// login
				toGetValidId(handler);
			}

			List<SsoUserFilter> ssoUserFilters = ApplicationContextUtil.getBeans(SsoUserFilter.class);
			for (SsoUserFilter ssoUserFilter : ssoUserFilters) {
				ssoUserFilter.filter(ssoUser);
			}
			SESSIONSPEC_SSOUSER.setValue(ssoUser);
		}
		UserHolder.setUser(ssoUser.getUser());

		if (StringUtil.hasText(RequestContext.getParameter(SsoUserInterceptor.PARAM_VALIDID))) {
			if ("127.0.0.1".equals(WebSetting.get().getServerName()) || "localhost".equals(WebSetting.get().getServerName())) {
				String cleanUrl = RequestContext.getRequestUrlWithQueryString();
				cleanUrl = UrlUtil.removeParam(cleanUrl, SsoUserInterceptor.PARAM_VALIDID);
				throw new RedirectException(cleanUrl);
			}
		}

		if (!checkPermissionUrl(RequestContext.getRequestURI(), ssoUser.getUser())) {
			throw new NoPermissionException("No Permission request :" + RequestContext.getRequestURI());
		}

		return super.preHandle(request, response, handler);
	}

	private User getUserWithValidId(String validId) {
		String userAgent = RequestContext.getHeader("User-Agent");
		if (StringUtil.hasText(userAgent) && userAgent.indexOf(FSAPP) >= 0) {
			return this.ssoUserServiceApp.getUserWithValidId(validId);
		}
		return ssoUserService.getUserWithValidId(validId);
	}

	private boolean checkPermissionUrl(String uri, User user) {
		if (null == uri || null == user || user.isSuperAdministrator())
			return true;
		Boolean permission = user.getPermissionUrlMap().get(uri);
		if (null != permission && permission == false) {
			return false;
		}
		return true;
	}

	private boolean isIgnoreRequestUrl() {
		String url = RequestContext.getRequestURI();
		if (null == url)
			return false;

		if (RequestContext.getRequestURL().toString().equals(EaSsoSetting.get().getValidUrl())) {
			if (null != UserHolder.getUser()) {
				logger.debug("ValidUrl request found user");
				return true;
			} else {
				logger.debug("ValidUrl request not found user");
			}
		}

		String[] urls = EaSsoSetting.get().getIgnoreUrls();
		for (int i = 0; null != urls && i < urls.length; i++) {
			if (url.equals(urls[i]))
				return true;
		}
		urls = EaSsoSetting.get().getIgnoreStartWithUrls();
		for (int i = 0; null != urls && i < urls.length; i++) {
			if (url.startsWith(urls[i]))
				return true;
		}
		return false;
	}

	private void toGetValidId(Object handler) {
		String returnUrl = RequestContext.getReferer();
		if (RequestContext.METHOD_GET.equalsIgnoreCase(RequestContext.getMethod()) && handler instanceof HandlerMethod) {
			HandlerMethod mandlerMethod = (HandlerMethod) handler;
			if (mandlerMethod.getBean() instanceof BaseViewController) {
				returnUrl = RequestContext.getRequestUrlWithQueryString();
				// RequestContext.getParameter(name)
			}
		}
		if (StringUtil.isEmpty(returnUrl)) {
			returnUrl = RequestContext.buildRequestUrl("/");
		}
		String validUrl = ConfigContext.getStringValue("sso.ea.validUrl");
		validUrl = UrlUtil.appendUrl(validUrl, "returnUrl", returnUrl);
		validUrl = UrlUtil.removeParam(validUrl, SsoUserInterceptor.PARAM_VALIDID);
		logger.debug("redirect to {}", validUrl);
		throw new RedirectException(validUrl);
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		super.afterCompletion(request, response, handler, ex);
		UserHolder.clear();
	}

	private void iniCookieSpec() {
		if (null == COOKIESPEC_VALIDID_ALL_DOMAIN) {
			COOKIESPEC_VALIDID_ALL_DOMAIN = buildVailIdCookieSpec();
		}
	}

}
